package com.baijia.authentication.handler.support;

import static com.baijia.authentication.AccountType.ORG;
import static com.baijia.authentication.AccountType.ORG_SUB_ACCT;

import com.baijia.authentication.AccountType;
import com.baijia.authentication.handler.exception.AuthenticationException;
import com.baijia.authentication.handler.exception.UrlMessageException;
import com.baijia.authentication.principal.Credentials;
import com.baijia.authentication.principal.credentials.PassportCredentials;
import com.baijia.dao.OrgAccountDao;
import com.baijia.dao.OrgCascadeAccountDao;
import com.baijia.dao.PassportDao;
import com.baijia.dao.UserDao;
import com.baijia.dao.po.OrgAccount;
import com.baijia.dao.po.OrgCascadeAccount;
import com.baijia.dao.po.Passport;
import com.baijia.dao.po.User;
import com.baijia.services.ServicesManager;
import com.baijia.services.impl.RegisteredServiceImpl;
import com.baijia.util.JigouPasswordUtil;
import com.baijia.util.RedisUtil;

import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;

import javax.annotation.Resource;

/**
 * Created by dudu on 16/4/26.
 */
@Component
public class PassportAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler {

    private static String JIGOU_PREFIX = "Jigou_mobile_";
    private static String JIGOU_ZI_PREFIX = "Jigou_zizhanghao_";
    private static String JIGOU_LOGIN_BAN_FLAG = "jigou_login_ban_flag";
    private static String JIGOU_ZI_LOGIN_BAN_FLAG = "Jigou_zizhanghao_login_ban_flag";
    private static String JIGOU_LOGIN_FAIL_TIMES = "Jigou_login_fail_times";
    private static String JIGOU_ZI_LOGIN_FAIL_TIMES = "Jigou_zizhanghao_login_fail_times";

    // private static String PREFIX = "passport_mobile_";
    // private static String LOGIN_BAN_FLAG = "passport_login_ban_flag";
    // private static String LOGIN_FAIL_TIMES = "passport_login_fail_times";

    @Resource
    private PassportDao passportDao;

    @Resource
    private OrgAccountDao orgAccountDao;

    @Resource
    private OrgCascadeAccountDao orgCascadeAccountDao;

    @Resource
    private UserDao userDao;

    @Autowired
    private ServicesManager servicesManager;

    @Override
    protected boolean preAuthenticate(Credentials credentials) throws AuthenticationException {
        PassportCredentials passportCredentials = (PassportCredentials) credentials;
        AccountType accountType = AccountType.getAccountTypeByCode(passportCredentials.getAccountType());
        String banFlag;
        switch (accountType) {
            case ORG:
                banFlag = RedisUtil.hGet(JIGOU_PREFIX + passportCredentials.getUsername(), JIGOU_LOGIN_BAN_FLAG);
                if (StringUtils.isNotBlank(banFlag)) {
                    if (BooleanUtils.toBoolean(banFlag)) {
                        throw new UrlMessageException("banned", "账号已被锁定，请一小时后再试");
                    }
                }
                break;
            case ORG_SUB_ACCT:
                banFlag = RedisUtil.hGet(JIGOU_ZI_PREFIX + passportCredentials.getUsername(), JIGOU_ZI_LOGIN_BAN_FLAG);
                if (StringUtils.isNotBlank(banFlag)) {
                    if (BooleanUtils.toBoolean(banFlag)) {
                        throw new UrlMessageException("banned", "账号已被锁定，请一小时后再试");
                    }
                }
                break;
            default:
                break;
        }

        return true;
    }

    @Override
    protected boolean postAuthenticate(final Credentials credentials, final boolean authenticated)
        throws AuthenticationException {
        PassportCredentials passportCredentials = (PassportCredentials) credentials;
        AccountType accountType = AccountType.getAccountTypeByCode(passportCredentials.getAccountType());
        String username = passportCredentials.getUsername();
        switch (accountType) {
            case ORG:
                if (!authenticated) {
                    RedisUtil.incrBy(JIGOU_PREFIX + username, JIGOU_LOGIN_FAIL_TIMES, 1L);
                    String failStr = RedisUtil.hGet(JIGOU_PREFIX + username, JIGOU_LOGIN_FAIL_TIMES);
                    long failTimes = Long.valueOf(failStr);
                    RegisteredServiceImpl service =
                        (RegisteredServiceImpl) servicesManager.findServiceBy(ORG.getCode());
                    if (failTimes >= service.getLoginMaxTimes()) {
                        RedisUtil.hSet(JIGOU_PREFIX + username, JIGOU_LOGIN_BAN_FLAG, Boolean.TRUE.toString(),
                            service.getBanSeconds());
                        throw new UrlMessageException("badCredential", "账号或密码错误" + failTimes + "次，还剩0次");
                    } else if (failTimes >= service.getLoginNoticeTimes()) {
                        throw new UrlMessageException("badCredential", "账号或密码错误" + failTimes + "次，还剩"
                            + (service.getLoginMaxTimes() - failTimes) + "次");
                    } else {
                        throw new UrlMessageException("badCredential", "账号或密码错误");
                    }
                } else {
                    RedisUtil.del((JIGOU_PREFIX + username).getBytes());
                }
                break;
            case ORG_SUB_ACCT:
                if (!authenticated) {
                    RedisUtil.incrBy(JIGOU_ZI_PREFIX + username, JIGOU_ZI_LOGIN_FAIL_TIMES, 1L);
                    String failStr = RedisUtil.hGet(JIGOU_ZI_PREFIX + username, JIGOU_ZI_LOGIN_FAIL_TIMES);
                    long failTimes = Long.valueOf(failStr);
                    RegisteredServiceImpl service =
                        (RegisteredServiceImpl) servicesManager.findServiceBy(ORG_SUB_ACCT.getCode());
                    if (failTimes >= service.getLoginMaxTimes()) {
                        RedisUtil.hSet(JIGOU_ZI_PREFIX + username, JIGOU_ZI_LOGIN_BAN_FLAG, Boolean.TRUE.toString(),
                            service.getBanSeconds());
                        throw new UrlMessageException("badCredential", "账号或密码错误" + failTimes + "次，还剩0次");
                    } else if (failTimes >= service.getLoginNoticeTimes()) {
                        throw new UrlMessageException("badCredential", "账号或密码错误" + failTimes + "次，还剩"
                            + (service.getLoginMaxTimes() - failTimes) + "次");
                    }

                } else {
                    RedisUtil.del((JIGOU_ZI_PREFIX + username).getBytes());
                }
                break;
            default:
                break;
        }

        return authenticated;
    }

    @Override
    protected boolean doAuthentication(Credentials credentials) throws AuthenticationException {
        PassportCredentials passportCredentials = (PassportCredentials) credentials;
        // 机构的登录名是手机号
        String mobile = passportCredentials.getUsername();
        String password = passportCredentials.getPassword();
        Integer appId = passportCredentials.getAccountType();
        AccountType accountType = AccountType.getAccountTypeByCode(appId);
        switch (accountType) {
            case ORG:
                OrgAccount orgAccount = orgAccountDao.findByMobile(mobile, passportCredentials.getCountryCode());
                if (orgAccount != null) {
                    try {
                        if (JigouPasswordUtil.validatePassword(password, orgAccount.getPassword())) {
                            return true;
                        }
                    } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
                        logger.warn("valid jigou password error! mobile:{}", mobile, e);
                    }
                }
                break;
            case ORG_SUB_ACCT:
                OrgCascadeAccount orgCascadeAccount = orgCascadeAccountDao.findByUsername(mobile);
                if (orgCascadeAccount != null) {
                    try {
                        if (JigouPasswordUtil.validatePassword(password, orgCascadeAccount.getPassword())) {
                            if (BooleanUtils.toBoolean(orgCascadeAccount.getStatus())) {
                                return true;
                            }
                        }

                    } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
                        logger.warn("valid jigou cascade account password error! username:{}", mobile, e);
                    }
                }
                break;
            case USER:
            case ORG_TEACHER:
                User user = userDao.getUserByMobile(mobile);
                try {
                    if (testLogin(password) || passportCredentials.isSms()
                        || JigouPasswordUtil.validatePassword(password, user.getPassword())) {
                        return true;
                    }
                } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
                    logger.warn("valid user password error! mobile:{}", mobile, e);
                }
                break;
            case PASSPORT:
            default:
                Passport passport = passportDao.findByMobile(mobile);
                try {
                    if (testLogin(password) || passportCredentials.isSms()
                        || JigouPasswordUtil.validatePassword(password, passport.getPassword())) {
                        return true;
                    }
                } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
                    logger.warn("valid user password error! mobile:{}", mobile, e);
                }
                break;
        }

        return false;

    }

    @Override
    public boolean supports(Credentials credentials) {
        return credentials instanceof PassportCredentials;
    }
}
